/*
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */
 
#include <fstream>

#include "BTreeIndex.h"
#include "BTreeNode.h"

using namespace std;

/*
 * BTreeIndex constructor
 */
BTreeIndex::BTreeIndex()
{
    ofstream myFile;
    myFile.open("hashindex.txt",ios::app);
    string rootline, hashkey, hashvalue;
    
    //read the first 4 bytes
    myFile.seekp(0, ios::end); // put the "cursor" at the end of the file
    int length = myFile.tellp(); // find the position of the cursor
    
    if(length==0)
    {
        rootPid= -1;
        myFile << rootPid << "\n";
        myFile.close();
    }
    else
    {
        myFile.close();
        ifstream myFile;
        myFile.open("hashindex.txt",ios::in);
        
        getline(myFile, rootline);
        rootPid = atoi(rootline.c_str());
        
            while (1) {
                
                getline(myFile, hashkey);
                getline(myFile, hashvalue);
                if(myFile.eof()) break;
                
                hashmap[atoi(hashkey.c_str())] = hashvalue;
                
            }
        
        
    }
    
}

/*
 * Open the index file in read or write mode.
 * Under 'w' mode, the index file should be created if it does not exist.
 * @param indexname[IN] the name of the index file
 * @param mode[IN] 'r' for read, 'w' for write
 * @return error code. 0 if no error
 */
RC BTreeIndex::open(const string& indexname, char mode)
{
    
    pf.open(indexname, 'w');
    
    
    return 0;
}

/*
 * Close the index file.
 * @return error code. 0 if no error
 */
RC BTreeIndex::close(const string& indexname)
{

    pf.close();
    
    return 0;
}

/*
 * Insert (key, RecordId) pair to the index.
 * @param key[IN] the key for the value inserted into the index
 * @param rid[IN] the RecordId for the record being inserted into the index
 * @return error code. 0 if no error
 */
RC BTreeIndex::insert(int key, const RecordId& rid) {
    /**
     * Open the index file
     */
    const string filename = "index.idx";

    pf.open(filename, 'w');
    
    if(rootPid == -1)
    {
        rootPid = pf.endPid();
        BTLeafNode b = new BTLeafNode(-1, rootPid, -1);
        pf.write(rootPid, b.getBuffer());
        replaceRootInFile();
    }

    rootPid = BTLeafNode::insert(key, &rid, &pf, rootPid);

    //UPDATE ROOTPID IN HASH-INDEX FILE
    replaceRootInFile();
    
    return 0;
}

void BTreeIndex:: replaceRootInFile()
{
    ofstream myFile;
    string s; //read each line of file
    myFile.open("hashindex.txt", ios::out);
    myFile.seekp(0);
    ifstream myFile;
    ofstream outfile;
    myFile.open("hashindex.txt", ios::in);
    outfile.open("temp.txt", ios::out);
    int flag = 0;
    while (getline(myFile, s, '\n')) {
        if (flag++ == 0)
            outfile << rootPid << "\n";
        else
            outfile << s << "\n";
    }
    myFile.close();
    outfile.close();
    rename("temp.txt", "hashindex.txt");
    
}

/*
 * Find the leaf-node index entry whose key value is larger than or 
 * equal to searchKey, and output the location of the entry in IndexCursor.
 * IndexCursor is a "pointer" to a B+tree leaf-node entry consisting of
 * the PageId of the node and the SlotID of the index entry.
 * Note that, for range queries, we need to scan the B+tree leaf nodes.
 * For example, if the query is "key > 1000", we should scan the leaf
 * nodes starting with the key value 1000. For this reason,
 * it is better to return the location of the leaf node entry 
 * for a given searchKey, instead of returning the RecordId
 * associated with the searchKey directly.
 * Once the location of the index entry is identified and returned 
 * from this function, you should call readForward() to retrieve the
 * actual (key, rid) pair from the index.
 * @param key[IN] the key to find.
 * @param cursor[OUT] the cursor pointing to the first index entry
 *                    with the key value.
 * @return error code. 0 if no error.
 */
RC BTreeIndex::locate(int searchKey, IndexCursor& cursor) {
    
    PageId pid = rootPid;
    RecordId rid;
    BTNonLeafNode b;
    void *key_sibling_buff;
    int eid = 1, op, flag, passedpid, passedeid, f, number_of_keys, siblingid; //op obtained from SqlEngine.cc

    while (hashmap[pid] != "L") {
        BTNonLeafNode::locateChildPtr(searchKey, &pid, &pf);
    }

    flag = BTLeafNode::locate(searchKey, &eid, pid, &pf, op);
    pf.read(pid, key_sibling_buff);
    memcpy(&number_of_keys, key_sibling_buff+2*sizeof(int), sizeof(int));
    memcpy(&siblingid, key_sibling_buff+3*sizeof(int), sizeof(int));
    
    if(eid > number_of_keys)
    {
        eid=1;
        pid = siblingid;
    }
    
    if(pid == -1) return -1;

    cursor.pid = pid;
    cursor.eid = eid;
    
    /*********************************END OF FUNCTION**********************************************/

    return 0;
}


/*
 * Read the (key, rid) pair at the location specified by the index cursor,
 * and move foward the cursor to the next entry.
 * @param cursor[IN/OUT] the cursor pointing to an leaf-node index entry in the b+tree
 * @param key[OUT] the key stored at the index cursor location.
 * @param rid[OUT] the RecordId stored at the index cursor location.
 * @return error code. 0 if no error
 */
RC BTreeIndex::readForward(IndexCursor& cursor, int& key, RecordId& rid)
{
    
    void *buffer;
    pf.read(cursor.pid, buffer);
    
    memcpy(&key, buffer+ 4*sizeof(int) + (cursor.eid-1)*sizeof(int), sizeof(int));
    memcpy(&rid.pid, buffer+ 4*sizeof(int) + (cursor.eid-1)*sizeof(int) + sizeof(int), sizeof(int));
    memcpy(&rid.sid, buffer+ 4*sizeof(int) + (cursor.eid-1)*sizeof(int) + 2*sizeof(int), sizeof(int));
    
    return 0;
}